#include "axl_config.h"

using namespace AXL_Projects;
using namespace std;

//statics
std::string Configuration::GlobalErrorString="";		//last error for use by all systems

void Configuration::LogEntry(const string& entry, bool wipe, string file)
{
	FILE *stream;
	if(!wipe) stream = fopen( file.c_str(), "a+t" );
	else stream = fopen( file.c_str(), "w+t" );

	if(stream!=NULL)
	{
		fprintf(stream,"%s\n",entry.c_str());
		fclose(stream);
	}
}

//standard member functions
Configuration::Configuration(const string& configfile)
{
	GlobalErrorString="";
	TiXmlElement* node;
	TiXmlElement* config;

	int _fps,_debugon,_wm, _wc, _ej,_ek,_em,_es, _emidi;
	int _gm, _vsync,_capgfx,_matchrefreshrate;
	int _dpref,_dfb,_sw,_sh,_smax, _svsamp, _svmusic;
	const char* _wpref;
	int _wfb;
	string _wprefs;

	//configfile can now be an XML string
	AllegroInitialised=false;
	LogEntry("-----------------------------------------");
	LogEntry("Configuration object created using file/buffer");
	this->ConfigFile=configfile;
	Configuration::GlobalErrorString="";
	
	TiXmlDocument tixml;
	if(configfile=="") 
	{
		GlobalErrorString="Configuration Filename/buffer not valid";
		LogEntry("ERROR: Configuration Failed. Filename not value",false);
		return;
	}

	//try to see if its a string first
	tixml.Parse(configfile.c_str());
	if(tixml.Error())
	{
		LogEntry("Entry failed to parse as a string so assuming it is a file");
		if(!tixml.LoadFile(configfile.c_str()))
		{
			LogEntry((string)"Initialised Configuration as a file failed. check it is valid XML:"+ tixml.ErrorDesc());
			GlobalErrorString="failed to load config as a file. Check it is valid XML";
			return;
		}
		else
			LogEntry("Initialised Configuration FILE using "+ConfigFile);

		LogEntry("Processing config (loaded and/or parsed ok). Checking attribute syntax.");
	}
	else
		LogEntry("Initialised Configuration as a string BUFFER ");

	config=tixml.RootElement();

	assert(config);
	if(!config)
	{
		GlobalErrorString="no root config element!";
		LogEntry("ERROR: no root config element!",false);
		return;
	}

	node=config->FirstChildElement("system")->ToElement();
	assert(node);
	if(!node)
	{
		GlobalErrorString="no system element!";
		LogEntry("ERROR: no system element!",false);
		return;
	}
		if(node->QueryIntAttribute("fps",&_fps)!=TIXML_SUCCESS) _fps=60;
		if(node->QueryIntAttribute("debugon",&_debugon)!=TIXML_SUCCESS) _debugon=0;
		if(node->QueryIntAttribute("autowritemain",&_wm)!=TIXML_SUCCESS) _wm=0;
		if(node->QueryIntAttribute("autowritecustom",&_wc)!=TIXML_SUCCESS) _wc=0;
		if(node->QueryIntAttribute("enablejoystick",&_ej)!=TIXML_SUCCESS) _ej=1;
		if(node->QueryIntAttribute("enablekeyboard",&_ek)!=TIXML_SUCCESS) _ek=1;
		if(node->QueryIntAttribute("enablemouse",&_em)!=TIXML_SUCCESS) _em=1;
		if(node->QueryIntAttribute("enablesound",&_es)!=TIXML_SUCCESS) _es=1;
		if(node->QueryIntAttribute("enablemidi",&_emidi)!=TIXML_SUCCESS) _emidi=1;
		if(node->QueryIntAttribute("matchrefreshrate",&_matchrefreshrate)!=TIXML_SUCCESS) _matchrefreshrate=0;

	node=config->FirstChildElement("graphics")->ToElement();
	assert(node);
	if(!node)
	{
		GlobalErrorString="no graphics element!";
		LogEntry("ERROR: no graphics element!",false);
		return;
	}
		if(node->QueryIntAttribute("vsync",&_vsync)!=TIXML_SUCCESS) _vsync=0;
		if(node->QueryIntAttribute("graphicsmode",&_gm)!=TIXML_SUCCESS) _gm=0;
		if(node->QueryIntAttribute("depthpreferred",&_dpref)!=TIXML_SUCCESS) _dpref=32;
		if(node->QueryIntAttribute("depthfallback",&_dfb)!=TIXML_SUCCESS) _dfb=16;
		if(node->QueryIntAttribute("capbmptype",&_capgfx)!=TIXML_SUCCESS) _capgfx=0;

	node=config->FirstChildElement("window")->ToElement();
	assert(node);
	if(!node)
	{
		GlobalErrorString="no window element!";
		LogEntry("ERROR: no window element!",false);
		return;
	}
		if(node->QueryIntAttribute("width",&_sw)!=TIXML_SUCCESS) _sw=800;
		if(node->QueryIntAttribute("height",&_sh)!=TIXML_SUCCESS) _sh=600;
		if(node->QueryIntAttribute("specified",&_wfb)!=TIXML_SUCCESS) _wfb=-1;
		_wpref=node->Attribute("autodetect");
			if(_wpref==NULL) _wprefs="";
			else _wprefs=(string)_wpref;

	node=config->FirstChildElement("sound")->ToElement();
	assert(node);
	if(!node)
	{
		GlobalErrorString="no sound element!";
		LogEntry("ERROR: no sound element!",false);
		return;
	}
		if(node->QueryIntAttribute("maxvoicearray",&_smax)!=TIXML_SUCCESS) _smax=32;
		if(node->QueryIntAttribute("samplevolume",&_svsamp)!=TIXML_SUCCESS) _svsamp=150;
		if(node->QueryIntAttribute("musicvolume",&_svmusic)!=TIXML_SUCCESS) _svmusic=128;

	//custom values
	const char* name;
	const char* valuestring;
	int valueint;
	float valuefloat;
	std::string namestring;

	ostringstream ss;
	TiXmlNode* nodule;

	for( nodule= config->FirstChild( "custom" );
		nodule;
		nodule = nodule->NextSibling( "custom" ) )
	{
		node=nodule->ToElement();
		name=node->Attribute("name");
		if(name==NULL) 
			LogEntry("ERROR: custom element found but had no name! check");
		else
		{
			bool found=false;
			namestring=name;
			valuestring=node->Attribute("valuestring");
			if(valuestring!=NULL)	
			{
				CustomString[name]=valuestring;
				found=true;
			}

			if(node->QueryDoubleAttribute("valuefloat",&valuefloat)==TIXML_SUCCESS)
			{
				found=true;
				CustomFloat[name]=valuefloat;
			}
			if(node->QueryIntAttribute("valueint",&valueint)==TIXML_SUCCESS)
			{
				CustomInt[name]=valueint;
				found=true;
			}

			if(!found) 
			{
				ss.str("");
				ss << "ERROR: Found custom element: " << name << " but there was not a valid value type";
				LogEntry(ss.str());
			}
		}

	}

	Initialise(_fps,_debugon==1,_wm==1,_wc==1,_ej==1,_ek==1,_em==1,_es==1,_emidi==1,
				  _vsync==1,_gm,_dpref,_dfb,_sw,_sh,_wprefs, _wfb,_smax, _svsamp, _svmusic,_capgfx==1,_matchrefreshrate==1);
}

void Configuration::Initialise(int fps, bool debugon,bool writemain, bool writecustom,
							 bool ejoy, bool ekey, bool emouse, bool esound, bool emidi,
							 bool vsync, int graphicsmode,int depthpreferred, int depthfallback,
							 int width, int height, const string& autodetectwindow, int fallbackwindow, 
							 int maxvoices, int soundvolume, int musicvolume,bool capgfx,bool matchrefreshrate)

{


	ostringstream ss;

	//set all stored values
	//system
	this->CapsSystem.DebugEnabled=debugon;
	this->CapsSystem.fps=fps;
	this->CapsSystem.UseJoystick=ejoy;
	this->CapsSystem.UseKeyboard=ekey;
	this->CapsSystem.UseMidi=emidi;
	this->CapsSystem.UseSound=esound;
	this->CapsSystem.UseMouse=emouse;
	this->CapsSystem.AutoWriteCustom=writecustom;
	this->CapsSystem.AutoWriteMain=writemain;
	this->CapsSystem.MatchRefreshRate=matchrefreshrate;
	//graphics
	this->CapsGraphics.ScrWidth=width;
	this->CapsGraphics.ScrHeight=height;
	this->CapsGraphics.Depth=depthpreferred;
	this->CapsGraphics.DepthFallback=depthfallback;
	switch(graphicsmode)
	{
	case 3:
		this->CapsGraphics.GraphicsMode=MODE_TRIPLE;
		break;
	case 2:
		this->CapsGraphics.GraphicsMode=MODE_PAGED;
		break;
	case 1:
		this->CapsGraphics.GraphicsMode=MODE_SYSTEMDOUBLE;
		break;
	default:
		this->CapsGraphics.GraphicsMode=MODE_DOUBLE;
	}
	this->CapsGraphics.VSync=vsync;
	this->CapsGraphics.WindowMode=autodetectwindow;
	this->CapsGraphics.WindowFallback=fallbackwindow;
	this->CapsGraphics.CapGraphicsToBufferType=capgfx;
	//sound
	this->CapsSound.MaxSounds=maxvoices;
	this->CapsSound.MusicVolume=musicvolume;
	this->CapsSound.SampleVolume=soundvolume;

	//check values
	if(fps<1 || fps>1000)
	{
		Configuration::LogEntry("ERROR: fps must be 1 to 1000. defaulting to 60.",false);
		this->CapsSystem.fps=60;
	}

	if(width<1)
	{
		Configuration::LogEntry("ERROR: width must be at least 1. defaulting to 600.",false);
		this->CapsGraphics.ScrWidth=600;
	}
	if(height<1)
	{
		Configuration::LogEntry("ERROR: height must be at least 1. defaulting to 800.",false);
		this->CapsGraphics.ScrHeight=800;
	}

	if(graphicsmode<0 || graphicsmode>3)
	{
		Configuration::LogEntry("ERROR: graphics mode invalid. defaulting to double buffer.",false);
		this->CapsGraphics.GraphicsMode=MODE_DOUBLE;
	}

	if(maxvoices<1 || maxvoices>255) this->CapsSound.MaxSounds=32;
	if(musicvolume<0 || musicvolume>255) this->CapsSound.MusicVolume=150;
	if(soundvolume<0 || soundvolume>255) this->CapsSound.SampleVolume=128;

	if(depthpreferred!=8 && depthpreferred!=16 && depthpreferred!=24 && depthpreferred!=32 && depthpreferred!=15)
		this->CapsGraphics.Depth=32;

	if(depthfallback!=8 && depthfallback!=16 && depthfallback!=24 && depthfallback!=32 && depthfallback!=15 && depthfallback!=0)
		this->CapsGraphics.DepthFallback=16;

	if(autodetectwindow!="any" && autodetectwindow!="windowed" && autodetectwindow!="fullscreen" && autodetectwindow!="")
		this->CapsGraphics.WindowMode="any";

	if(fallbackwindow<0 && autodetectwindow=="")
	{
		Configuration::LogEntry("ERROR: auto graphics is off and there is no gfx override, defaulting to any",false);
		this->CapsGraphics.WindowMode="any";
	}
	if(fallbackwindow<0) this->CapsGraphics.WindowFallback=-1;

	//below is lazy code for those who would rather copy/paste and change than write
	//a function to do it :)
	ss.str("");
	ss << "\tfps: wanted " << fps << " got " << this->CapsSystem.fps;
	LogEntry(ss.str(),false);

	ss.str("");
	ss << "\tMatch Refresh Rate: wanted " << matchrefreshrate << " got " << this->CapsSystem.MatchRefreshRate;
	LogEntry(ss.str(),false);

	ss.str("");
	ss << "\tDebug: wanted " << debugon << " got " << this->CapsSystem.DebugEnabled;
	LogEntry(ss.str(),false);

	ss.str("");
	ss << "\tMouse: wanted " << emouse << " got " << this->CapsSystem.UseMouse;
	LogEntry(ss.str(),false);

	ss.str("");
	ss << "\tKeyboard: wanted " << ekey << " got " << this->CapsSystem.UseKeyboard;
	LogEntry(ss.str(),false);

	ss.str("");
	ss << "\tJoystick: wanted " << ejoy << " got " << this->CapsSystem.UseJoystick;
	LogEntry(ss.str(),false);

	ss.str("");
	ss << "\tSound: wanted " << esound << " got " << this->CapsSystem.UseSound;
	LogEntry(ss.str(),false);

	ss.str("");
	ss << "\tMidi: wanted " << emidi << " got " << this->CapsSystem.UseMidi;
	LogEntry(ss.str(),false);

	ss.str("");
	ss << "\tAutoWriteMain: wanted " << writemain << " got " << this->CapsSystem.AutoWriteMain;
	LogEntry(ss.str(),false);

	ss.str("");
	ss << "\tAutoWriteCustom: wanted " << writecustom << " got " << this->CapsSystem.AutoWriteCustom;
	LogEntry(ss.str(),false);

	ss.str("");
	ss << "\tWidth: wanted " << width << " got " << this->CapsGraphics.ScrWidth;
	LogEntry(ss.str(),false);

	ss.str("");
	ss << "\tHeight: wanted " << height << " got " << this->CapsGraphics.ScrHeight;
	LogEntry(ss.str(),false);

	ss.str("");
	ss << "\tVsync: wanted " << vsync << " got " << this->CapsGraphics.VSync;
	LogEntry(ss.str(),false);

	ss.str("");
	ss << "\tDepth: wanted " << depthpreferred << " got " << this->CapsGraphics.Depth;
	LogEntry(ss.str(),false);

	ss.str("");
	ss << "\tDepthFallback: wanted " << depthfallback << " got " << this->CapsGraphics.DepthFallback;
	LogEntry(ss.str(),false);

	ss.str("");
	ss << "\tWindowMode: wanted " << autodetectwindow << " got " << this->CapsGraphics.WindowMode;
	LogEntry(ss.str(),false);

	ss.str("");
	ss << "\tWindowFallback: wanted " << fallbackwindow << " got " << this->CapsGraphics.WindowFallback;
	LogEntry(ss.str(),false);

	ss.str("");
	ss << "\tGraphicsMode: wanted " << graphicsmode << " got " << this->CapsGraphics.GraphicsMode;
	LogEntry(ss.str(),false);

	ss.str("");
	ss << "\tMaxSamples: wanted " << maxvoices << " got " << this->CapsSound.MaxSounds;
	LogEntry(ss.str(),false);

	ss.str("");
	ss << "\tSoundVolume: wanted " << soundvolume << " got " << this->CapsSound.SampleVolume;
	LogEntry(ss.str(),false);


	ss.str("");
	ss << "\tMusicVolume: wanted " << musicvolume << " got " << this->CapsSound.MusicVolume;
	LogEntry(ss.str(),false);

	LogEntry("\n---------------");
	LogEntry("Custom Strings");
	map<string,string>::const_iterator iCustomString;
	for(iCustomString=this->CustomString.begin();iCustomString!=CustomString.end();iCustomString++)
	{
		ss.str("");
		ss << (*iCustomString).first << " has the value " << (*iCustomString).second;
		LogEntry(ss.str());
	}
	LogEntry("\n---------------");
	LogEntry("Custom Ints");
	map<string,int>::const_iterator iCustomInt;
	for(iCustomInt=this->CustomInt.begin();iCustomInt!=CustomInt.end();iCustomInt++)
	{
		ss.str("");
		ss << (*iCustomInt).first << " has the value " << (*iCustomInt).second;
		LogEntry(ss.str());
	}

	LogEntry("\n---------------");
	LogEntry("Custom Floats");
	map<string,float>::const_iterator iCustomFloat;
	for(iCustomFloat=this->CustomFloat.begin();iCustomFloat!=CustomFloat.end();iCustomFloat++)
	{
		ss.str("");
		ss << (*iCustomFloat).first << " has the value " << (*iCustomFloat).second;
		LogEntry(ss.str());
	}

	LogEntry("\nEND OF CONFIGURATION INITIALISATION\n");
	LogEntry("-----------------------------------------");
}

Configuration::~Configuration(void)
{
	if(this->CapsSystem.AutoWriteMain)
		FlushMain();
	else
		LogEntry("Auto Flush main off so not writing back main values. Assumes manual Flush");

	if(this->CapsSystem.AutoWriteCustom)
		FlushCustom();
	else
		LogEntry("Auto Flush custom off so not writing back main values. Assumes manual Flush");

	this->CustomFloat.clear();
	this->CustomInt.clear();
	this->CustomString.clear();
	Configuration::LogEntry("allegro destructor called.", false, "config.log");
}

bool Configuration::FlushCustom(const string& filename)
{
	bool useBuffer=false;
	map<string,string>::const_iterator iCustomString;
	map<string,int>::const_iterator iCustomInt;
	map<string,float>::const_iterator iCustomFloat;
	string NewFile;

	LogEntry("Flushing Custom");
	if(filename=="" && ConfigFile=="") 
	{
		LogEntry("ERROR: No filename supplied for custom flush but there was no config file set in the configuration using 'config.xml'");
		NewFile="config.xml";
	}
	else
	{
		if(filename!="") NewFile=filename;
		else NewFile=ConfigFile;
	}

	//check if file or string
	TiXmlDocument doc;
	doc.Parse(NewFile.c_str());
	if(doc.Error())
	{
		//filename
		if(!doc.LoadFile(NewFile.c_str()))
		{
			//no file
			const char* FileContent =
					"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
					"<config>"
					"  <system  />"
					"  <graphics />"
					"  <window  />"
					"  <sound />"
					"</config>";
			doc.Parse(FileContent);
		}
	}
	else useBuffer=true;
	TiXmlElement* config=doc.RootElement();
	

	//delete all custom nodes then add them. seems the easiest way
	TiXmlNode* nodule;
	TiXmlNode* temp;
	nodule= config->FirstChild( "custom" );
	while(nodule)
	{
		temp=nodule->NextSibling("custom");
		config->RemoveChild(nodule);
		nodule=temp;
	}

	LogEntry("Writing Custom Strings");
	for(iCustomString=this->CustomString.begin();iCustomString!=CustomString.end();iCustomString++)
	{
		TiXmlElement item( "custom" );
		item.SetAttribute("name",(*iCustomString).first.c_str());
		item.SetAttribute("valuestring",((*iCustomString).second).c_str());
		config->InsertEndChild(item);
	}

	LogEntry("Custom Ints");
	for(iCustomInt=this->CustomInt.begin();iCustomInt!=CustomInt.end();iCustomInt++)
	{
		TiXmlElement item( "custom" );
		item.SetAttribute("name",(*iCustomInt).first.c_str());
		item.SetAttribute("valueint",((*iCustomInt).second));
		config->InsertEndChild(item);
	}

	LogEntry("Custom Floats");
	for(iCustomFloat=this->CustomFloat.begin();iCustomFloat!=CustomFloat.end();iCustomFloat++)
	{
		TiXmlElement item( "custom" );
		item.SetAttribute("name",(*iCustomFloat).first.c_str());
		item.SetDoubleAttribute("valuefloat",((*iCustomFloat).second));
		config->InsertEndChild(item);
	}
	if(!useBuffer) doc.SaveFile();
	return true;
}

bool Configuration::FlushMain(const string& filename)
{
	bool useBuffer=false;
	string NewFile;

	LogEntry("Flushing Main");
	if(filename=="" && ConfigFile=="") 
	{
		LogEntry("ERROR: No filename supplied for main flush but there was no config file set in the configuration. using 'config.xml'");
		NewFile="config.xml";
	}
	else
	{
		if(filename!="") NewFile=filename;
		else NewFile=ConfigFile;
	}

	TiXmlDocument doc;
	doc.Parse(NewFile.c_str());
	if(doc.Error())
	{
		if(!doc.LoadFile(NewFile.c_str()))
		{
			//no file
			const char* FileContent =
					"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
					"<config>"
					"  <system  />"
					"  <graphics />"
					"  <window  />"
					"  <sound />"
					"</config>";
			doc.Parse(FileContent);
		}
	}
	else useBuffer=true;
	TiXmlElement* config=doc.RootElement();

	//delete all nodes then add them. seems the easiest way
	//only problem is we end up with a full config even if
	//none where changed or some were not set
	TiXmlNode* nodule;
	nodule= config->FirstChild( "system" );
	if(nodule) config->RemoveChild(nodule);
	nodule= config->FirstChild( "graphics" );
	if(nodule) config->RemoveChild(nodule);
	nodule= config->FirstChild( "window" );
	if(nodule) config->RemoveChild(nodule);
	nodule= config->FirstChild( "sound" );
	if(nodule) config->RemoveChild(nodule);

	LogEntry("Writing Main System");
	TiXmlElement item( "system" );
	item.SetAttribute("fps",this->CapsSystem.fps);
	item.SetAttribute("debugon",this->CapsSystem.DebugEnabled);
	item.SetAttribute("autowritemain",this->CapsSystem.AutoWriteMain);
	item.SetAttribute("autowritecustom",this->CapsSystem.AutoWriteCustom);
	item.SetAttribute("enablejoystick",this->CapsSystem.UseJoystick);
	item.SetAttribute("enablekeyboard",this->CapsSystem.UseKeyboard);
	item.SetAttribute("enablemouse",this->CapsSystem.UseMouse);
	item.SetAttribute("enablesound",this->CapsSystem.UseSound);
	item.SetAttribute("enablemidi",this->CapsSystem.UseMidi);
	item.SetAttribute("matchrefreshrate",this->CapsSystem.MatchRefreshRate);
	config->InsertEndChild(item);

	LogEntry("Writing Main Graphics");
	TiXmlElement item2( "graphics" );
	item2.SetAttribute("vsync",this->CapsGraphics.VSync);
	item2.SetAttribute("graphicsmode",(int)(this->CapsGraphics.GraphicsMode));
	item2.SetAttribute("depthpreferred",this->CapsGraphics.Depth);
	item2.SetAttribute("depthfallback",this->CapsGraphics.DepthFallback);
	item2.SetAttribute("capbmptype",this->CapsGraphics.CapGraphicsToBufferType);
	config->InsertEndChild(item2);

	LogEntry("Writing Main Window");
	TiXmlElement item3( "window" );
	item3.SetAttribute("width",this->CapsGraphics.ScrWidth);
	item3.SetAttribute("height",this->CapsGraphics.ScrHeight);
	if(this->CapsGraphics.WindowFallback==-1)
		item3.SetAttribute("autodetect",this->CapsGraphics.WindowMode.c_str());
	else
		item3.SetAttribute("specified",this->CapsGraphics.WindowFallback);
	config->InsertEndChild(item3);

	LogEntry("Writing Main Sound");
	TiXmlElement item4( "sound" );
	item4.SetAttribute("maxvoicearray",this->CapsSound.MaxSounds);
	item4.SetAttribute("samplevolume",this->CapsSound.SampleVolume);
	item4.SetAttribute("musicvolume",this->CapsSound.MusicVolume);
	config->InsertEndChild(item4);

	if(!useBuffer) doc.SaveFile();
	return true;
}

int Configuration::GetCustom(const std::string& name,const int defaultvalue) const
{
	map<string,int>::const_iterator iFind;
	iFind=this->CustomInt.find(name);
	if(iFind==CustomInt.end()) return defaultvalue;
	else return (*iFind).second;
}

float Configuration::GetCustom(const std::string& name,const float defaultvalue) const
{
	map<string,float>::const_iterator iFind;
	iFind=this->CustomFloat.find(name);
	if(iFind==CustomFloat.end()) return defaultvalue;
	else return (*iFind).second;
}

const std::string& Configuration::GetCustom(const std::string& name, const std::string& defaultvalue) const
{
	map<string,string>::const_iterator iFind;
	iFind=this->CustomString.find(name);
	if(iFind==CustomString.end()) return defaultvalue;
	else return (*iFind).second;
	return defaultvalue;
}

//have to flush to write to file
void Configuration::SetCustom(const std::string& name, const int value)
{
	map<string,int>::iterator iFind;
	iFind=this->CustomInt.find(name);
	if(iFind!=CustomInt.end()) CustomInt.erase(iFind);
	CustomInt[name]=value;
}

void Configuration::SetCustom(const std::string& name, const float value)
{
	map<string,float>::iterator iFind;
	iFind=this->CustomFloat.find(name);
	if(iFind!=CustomFloat.end()) CustomFloat.erase(iFind);
	CustomFloat[name]=value;
}
void Configuration::SetCustom(const std::string& name, const std::string& value)
{
	map<string,string>::iterator iFind;
	iFind=this->CustomString.find(name);
	if(iFind!=CustomString.end()) CustomString.erase(iFind);
	CustomString[name]=value;
}

int Configuration::AllegroStart()
{
	//all checks on valid values are initially done on reading the XML
	//subsequent changes to these to invalid entries by the user are not checked
	//this function is re-entrant so have to perform cleanup if re-initialising
	
	int SuccessCode=2;		//default to fully successful
	int Ret;
	ostringstream ss;

	//if already set up, just log it as nothing needs to be specially freed, etc.
	if(AllegroInitialised) 
	{
		Configuration::LogEntry("***Allegro is being re-initialised***");
		//if using the framework or animation, they must delete all bitmaps first and reload them to guarantee 
		//they are correct
		set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
		AllegroInitialised=false;
	}
	else
	{	
		//set up allegro excluding graphics mode
		AllegroInitialised=false;
		
		srand(time(NULL)); //one and only seed
		Configuration::LogEntry("------------------------------");
		Configuration::LogEntry("Allegro system starting");
		Configuration::LogEntry("------------------------------");
	
		Ret=allegro_init();
		if(Ret)
		{
			ss << "allegro_init failed with " << Ret;
			Configuration::LogEntry(ss.str());
			Configuration::GlobalErrorString=ss.str();
			Configuration::LogEntry("------------------------------");
			Configuration::LogEntry("Allegro system startup failed");
			Configuration::LogEntry("------------------------------");
			return 0;
		}
		
		ss.str("");
		Ret=install_timer();
		if(Ret)
		{
			ss << "install_timer failed with " << Ret;
			Configuration::LogEntry(ss.str());
			Configuration::GlobalErrorString=ss.str();
			Configuration::LogEntry("------------------------------");
			Configuration::LogEntry("Allegro system startup failed");
			Configuration::LogEntry("------------------------------");
			return 0;
		}
	
		if(this->CapsSystem.UseKeyboard)
		{
			install_keyboard();
			LogEntry("initialise keyboard");
		}
		else
			LogEntry("keyboard skipped");
	
		if(this->CapsSystem.UseJoystick)
		{
			install_joystick(JOY_TYPE_AUTODETECT);
			LogEntry("initialise joystick",false);
		}
		else
			LogEntry("joystick skipped",false);
	
		if(this->CapsSystem.UseMouse)
		{
			install_mouse();
			LogEntry("initialise mouse");
		}
		else
			LogEntry("mouse skipped",false);
	
		//sound
		int _midi=DIGI_NONE;
		int _sound=MIDI_NONE;
		CapsActualSystem.UseSound=CapsActualSystem.UseMidi=false;
		if(this->CapsSystem.UseSound)
		{
			if(detect_digi_driver(DIGI_AUTODETECT)<=0) 
			{
				LogEntry("ERROR: Sound samples not available - detect_digi failed");
				//arent return fail so only update if not already set
				SuccessCode=1;
			}
			else
			{
				this->CapsActualSystem.UseSound=true;
				LogEntry("Sound samples available - detect_digi success");
				_sound=DIGI_AUTODETECT;
			}
		}
		if(this->CapsSystem.UseMidi)
		{
			if(detect_midi_driver(MIDI_AUTODETECT)<=0)
			{
				LogEntry("ERROR: Sound MIDI not available - detect_midi failed");
				SuccessCode=1;
			}
			else
			{
				this->CapsActualSystem.UseMidi=true;
				LogEntry("Sound MIDI available - detect_midi success");
				_midi=MIDI_AUTODETECT;
			}
		}
		
		if(this->CapsActualSystem.UseMidi || this->CapsActualSystem.UseSound)
		{
			//initialise sound/midi
			//if not there then will use dummy
			reserve_voices(this->CapsSound.MaxSounds,this->CapsSound.MaxSounds);
			if(install_sound(_sound, _midi,NULL)==0) 
			{
				Configuration::LogEntry("Sound installed correctly");
				set_volume(255,255); //maximum to whatever card is, volume controlled in game
									 //you use the volume values in your play routines
				this->CapsActualSystem.UseSound=this->CapsActualSystem.UseSound && (_sound==DIGI_AUTODETECT);
				this->CapsActualSystem.UseMidi=this->CapsActualSystem.UseMidi && (_midi==MIDI_AUTODETECT);
				ss.str("");
				if(this->CapsActualSystem.UseSound) Configuration::LogEntry("Actual UseSound is true. Fully Successful");
				if(this->CapsActualSystem.UseMidi) Configuration::LogEntry("Actual UseMidi is true. Fully Successful");
			}
			else
			{
				this->CapsActualSystem.UseSound=false;
				this->CapsActualSystem.UseMidi=false;
				LogEntry("ERROR: Sound not available. was detected but not initialised");
				SuccessCode=1;
			}
		}
	}
	
	//graphics
	ss.str("");
	if(!this->CapsGraphics.CapGraphicsToBufferType)
		Configuration::LogEntry("Graphics will not be capped, e.g. slow VRAM->MEMORY will be honoured");
	else
		Configuration::LogEntry("Any animation/graphics will be capped to whatever the screen buffer type is");
	
	set_color_depth(this->CapsGraphics.Depth);
	ss << "depth set to " << this->CapsGraphics.Depth;
	Configuration::LogEntry(ss.str());

	int mode;
	int h=this->CapsGraphics.ScrHeight;
	int w=this->CapsGraphics.ScrWidth;
	string windowfirst=this->CapsGraphics.WindowMode;
	int windowspecified=this->CapsGraphics.WindowFallback;
	//auto
	ss.str("");
	//ss << "debug spec: " << windowspecified;
	
	//set refresh rate - function handles daft numbers if needs must
	if(this->CapsSystem.MatchRefreshRate)
		request_refresh_rate(this->CapsSystem.fps);
	
	if(windowspecified==-1)
	{
		ss << "Graphic selected as " << this->CapsGraphics.WindowMode << ". w/h set as " << w << "/" << h;
		Configuration::LogEntry(ss.str());
		//get first mode
		Configuration::LogEntry("auto mode first tried");
		if(windowfirst=="windowed") 
		{
			Configuration::LogEntry("windowed mode selected");
			mode=GFX_AUTODETECT_WINDOWED;
		}
		else if(windowfirst=="fullscreen") 
		{
			Configuration::LogEntry("fullscreen mode selected");
			mode=GFX_AUTODETECT_FULLSCREEN;
		}
		else
		{
			Configuration::LogEntry("full auto mode selected");
			mode=GFX_AUTODETECT;
		}
		
		//try the preferred mode
		if(set_gfx_mode(mode, w,h, 0,0)<0)
		{
			SuccessCode=1;
			if(windowfirst=="windowed") mode=GFX_AUTODETECT_FULLSCREEN;
			else if(windowfirst=="fullscreen") mode=GFX_AUTODETECT_WINDOWED;
			else mode=-1;
			//try alternative if any not used
			if(mode!=-1)
			{
				SuccessCode=1;
				Configuration::LogEntry("full/windowed mode failed, trying alternate (i.e. windowed/fullscreen)");
				if(set_gfx_mode(mode,w,h,0,0)<0)
				{
					Configuration::LogEntry("other auto mode failed. trying fallback depth");
					mode=-1;
				}
				else
				{
					Configuration::LogEntry("alternate window mode was ok");
				}
			}
			if(mode==-1 )
			{
				//try fallback depth
				if(this->CapsGraphics.DepthFallback==0)
				{
					Configuration::LogEntry("ERROR: auto modes failed and no fallback depth set. aborting");
					Configuration::LogEntry("------------------------------");
					Configuration::LogEntry("Allegro system startup failed");
					Configuration::LogEntry("------------------------------");
					return 0;
				}

				ss.str("");
				ss << "fallback depth of " << this->CapsGraphics.WindowFallback << " being tried";
				Configuration::LogEntry(ss.str());
				set_color_depth(this->CapsGraphics.WindowFallback);
				if(windowfirst=="windowed") 
				{
					Configuration::LogEntry("windowed mode selected");
					mode=GFX_AUTODETECT_WINDOWED;
				}
				else if(windowfirst=="fullscreen") 
				{
					Configuration::LogEntry("fullscreen mode selected");
					mode=GFX_AUTODETECT_FULLSCREEN;
				}
				else
				{
					Configuration::LogEntry("full auto mode selected");
					mode=GFX_AUTODETECT;
				}
				//try the preferred mode
				if(set_gfx_mode(mode, w, h, 0,0)<0)
				{
					Configuration::LogEntry("mode failed, seeing if others available from config");
					if(windowfirst=="windowed") mode=GFX_AUTODETECT_FULLSCREEN;
					else if(windowfirst=="fullscreen") mode=GFX_AUTODETECT_WINDOWED;
					else mode=-1;
					//try alternative if any not used
					if(mode!=-1)
					{
						Configuration::LogEntry("trying other auto mode");
						if(set_gfx_mode(mode,w,h,0,0)<0)
						{
							mode=-1;
						}
					}
					if(mode==-1)
					{
						Configuration::LogEntry("ERROR: all modes failed. aborting");
						Configuration::LogEntry("------------------------------");
						Configuration::LogEntry("Allegro system startup failed");
						Configuration::LogEntry("------------------------------");
						return 0;
					}
				}
				else
					this->CapsActualSystem.DepthUsed=this->CapsGraphics.DepthFallback;
				
			}
		}
		else
			this->CapsActualSystem.DepthUsed=this->CapsGraphics.Depth;
	}
	else
	{
		ss.str("");
		ss << "Graphic selected as manual mode " << this->CapsGraphics.WindowFallback << ". w/h set as " << this->CapsGraphics.ScrWidth << "/" << this->CapsGraphics.ScrHeight;
		Configuration::LogEntry(ss.str());
		Configuration::LogEntry("auto mode turned off, trying specified mode");
		mode=this->CapsGraphics.WindowFallback;
		if(set_gfx_mode(mode, w, h, 0,0)<0)
		{
			SuccessCode=1;
			Configuration::LogEntry("mode failed, trying alternative depth");
			if(this->CapsGraphics.DepthFallback==0)
			{
				Configuration::LogEntry("specified mode failed and no fallback depth set. aborting");
				Configuration::GlobalErrorString="specified graphics mode failed and no fallback depth set.";
				Configuration::LogEntry("------------------------------");
				Configuration::LogEntry("Allegro system startup failed");
				Configuration::LogEntry("------------------------------");
				return 0;
			}
			else
			{
				set_color_depth(this->CapsGraphics.DepthFallback);
				if(set_gfx_mode(mode, w, h, 0,0)<0)
				{
					Configuration::LogEntry("all modes failed. aborting");
					Configuration::GlobalErrorString="all graphics modes failed. aborting";
					Configuration::LogEntry("------------------------------");
					Configuration::LogEntry("Allegro system startup failed");
					Configuration::LogEntry("------------------------------");
					return 0;
				}
				else
					this->CapsActualSystem.DepthUsed=this->CapsGraphics.DepthFallback;
			}
		}
		else
		{
			Configuration::LogEntry("Manual mode set ok as specified");
			this->CapsActualSystem.DepthUsed=this->CapsGraphics.Depth;
		}
	}

//some defaults as they seem nice enough

#if((ALLEGRO_VERSION==4 && ALLEGRO_SUB_VERSION>0) || ALLEGRO_VERSION>4)	
{
	//supports _ex method of text transparency. no need to do anything
	Configuration::LogEntry("Newer Allegro system that does text transparency inline found.");
}
#else
{
	//don't know if this was when it was changed to _ex functions
	Configuration::LogEntry("Older system that requires text_mode for text transparency detected");
	text_mode(-1);
}
#endif
	
	//true colour, used if required
	set_trans_blender(150,150,150,230);

	//8 bit stuff, used if required
	set_palette(desktop_palette);
	create_blender_table(&game_colour_map, desktop_palette, NULL);
	color_map=&game_colour_map;

	int returnedrefresh=get_refresh_rate();
	
	ss.str("");
	if(returnedrefresh==0) Configuration::LogEntry("Refresh rate of 0 below means system could not determine it, so any change probably didn't work");
	if(!this->CapsSystem.MatchRefreshRate)
		ss << "No change required, however system returned the refresh rate as " << returnedrefresh;
	else
		ss << "Wanted to get refresh rate of " << this->CapsSystem.fps << ". Got " << returnedrefresh;
		
	Configuration::LogEntry(ss.str());

	//set default switchback mode. probably needs changing by code
	if(set_display_switch_mode(SWITCH_PAUSE)==0)
		Configuration::LogEntry("Switch mode set to PAUSE");
	else
	{
		Configuration::LogEntry("Switch mode failed to set to PAUSE. Trying SWITCH_AMNESIA");
		if(set_display_switch_mode(SWITCH_AMNESIA)==0)
			Configuration::LogEntry("Switch mode set to SWITCH_AMNESIA - pause/resume but buffer needs refreshing on focus");
		else
		{
			Configuration::LogEntry("Switch mode failed to set to SWITCH_AMNESIA. Trying SWITCH_BACKAMNESIA");
			if(set_display_switch_mode(SWITCH_BACKAMNESIA)==0)
				Configuration::LogEntry("Switch mode set to SWITCH_AMNESIA - run in background but buffer needs refreshing on focus");
			else
				Configuration::LogEntry("Switch mode failed to set SWITCH_BACKAMNESIA. Nothing Done!");
		}
	}
	
	this->AllegroInitialised=true;
	
		Configuration::LogEntry("------------------------------");
		Configuration::LogEntry("Allegro system startup successful");
		Configuration::LogEntry("------------------------------");
	return SuccessCode;
}
